package com.plum.admin.common.aspect;

import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.plum.admin.common.event.LogEvent;
import com.plum.admin.common.util.IpUtil;
import com.plum.admin.common.util.LogUtil;
import com.plum.admin.common.util.SecurityUtil;
import com.plum.admin.modules.sys.entity.SysLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;

/**
 * TODO
 *
 * @author 子丶沫
 * @version 1.0
 * @date 2021/8/13 14:55
 */
@Slf4j
@Component
@Aspect
public class LogAspect {
    /**
     * log实体类
     **/
    private final ThreadLocal<SysLog> sysLogThreadLocal = new ThreadLocal<>();
    @Autowired
    private ApplicationEventPublisher publisher;
    @Autowired
    private SecurityUtil securityUtil;
    @Autowired
    private IpUtil ipUtil;
    @Value("${plum.baidu.location.key}")
    private String key;
    @Pointcut("@annotation(com.plum.admin.common.annotation.Log)")
    public void sysLogAspect() {}

    @Before(value = "sysLogAspect()")
    public void beforeLogHandler(JoinPoint joinPoint) throws Throwable {
        SysLog sysLog = new SysLog();
        sysLogThreadLocal.set(sysLog);
        Map<String, String> location = ipUtil.getLocation(key);
        long beginTime = Instant.now().toEpochMilli();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        sysLog.setUserName(securityUtil.getUsername());
        sysLog.setActionUrl(URLUtil.getPath(request.getRequestURI()));
        sysLog.setStartTime(LocalDateTime.now());
        sysLog.setRequestIp(location.get("ip"));
        sysLog.setLocation(location.get("location"));
        sysLog.setRequestMethod(request.getMethod());
        sysLog.setUa(request.getHeader("user-agent"));
        //访问目标方法的参数 可动态改变参数值
        Object[] args = joinPoint.getArgs();
        // 类名
        sysLog.setClassPath(joinPoint.getTarget().getClass().getName());
        //获取执行的方法名
        String name = joinPoint.getSignature().getName();
        sysLog.setActionMethod(name);
        sysLog.setFinishTime(LocalDateTime.now());
        // 参数
        sysLog.setParams(Arrays.toString(args));
        // 获取操作类型
        sysLog.setOperateType((int)LogUtil.getControllerMethod(joinPoint).get("type"));
        sysLog.setDescription((String) LogUtil.getControllerMethod(joinPoint).get("title"));
        sysLog.setCreateTime(LocalDateTime.now());
        long endTime = Instant.now().toEpochMilli();
        sysLog.setConsumingTime(endTime - beginTime);

    }
    @AfterReturning(returning = "ret",pointcut = "sysLogAspect()")
    public void afterLogHandler(Object ret){
        SysLog sysLog = sysLogThreadLocal.get();
        sysLog.setType("1");
        publisher.publishEvent(new LogEvent(this,sysLog));
        sysLogThreadLocal.remove();
    }
    @AfterThrowing(pointcut = "sysLogAspect()",throwing = "e")
    public void  errorLogHandler(Throwable e){
        SysLog sysLog = sysLogThreadLocal.get();
        sysLog.setType("2");
        sysLog.setExDetail(LogUtil.getStackTrace(e));
        // 异常信息
        sysLog.setExDesc(e.getMessage());
        publisher.publishEvent(new LogEvent(this,sysLog));
        sysLogThreadLocal.remove();
    }
}
